package code.jzh.monitor.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import code.jzh.monitor.entity.methodinvoke.Anchor;
import code.jzh.monitor.entity.methodinvoke.MessageInvokeRecord;
import code.jzh.monitor.entity.methodinvoke.MethodChain;

public class MethodMonitorUtil {
	private static final ThreadLocal<Map<String, Object>> threadInfo = new ThreadLocal<Map<String, Object>>();
	
	private static final String callID = "callID";
	private static final String depth = "depth";
	private static final String anchor = "anchor";
	
	public static String getCallID(){
		init();
		return (String)threadInfo.get().get(callID);
	}
	
	public static int getDepth(){
		init();
		return (Integer)threadInfo.get().get(depth);
	}
	
	public static void addDepth(){
		init();
		int curDepth = (Integer)threadInfo.get().get(depth);
		threadInfo.get().put(depth, curDepth+1);
	}
	
	public static void subDepth(){
		init();
		int curDepth = (Integer)threadInfo.get().get(depth);
		threadInfo.get().put(depth, curDepth-1);
	}
	
	public static List<Anchor> getAnchor(){
		init();
		return (List<Anchor>)threadInfo.get().get(anchor);
	}
	
	public static Anchor getLastAnchor(){
		init();
		List<Anchor> list = (List<Anchor>)threadInfo.get().get(anchor);
		if(list.size()>0){
			return list.get(list.size()-1);
		}
		return null;
	}
	
	public static void putAnchor(Anchor an){
		init();
		List<Anchor> anchors = (List<Anchor>)threadInfo.get().get(anchor);
		anchors.add(an);
	}
	
	public static void remove(){
		if(threadInfo.get()!=null)
			threadInfo.get().clear();
		threadInfo.remove();
	}
	
	public static void init(){
		if(threadInfo.get()!=null)
			return;
		Map<String, Object> map = new HashMap<String, Object>();
		long curr = System.currentTimeMillis();
		Random random = new Random(curr);
		map.put(callID, curr+"|"+random.nextLong());
		map.put(depth, 0);
		map.put(anchor, new ArrayList<Anchor>());
		threadInfo.set(map);
	}
	
	public static List<MessageInvokeRecord> buildMethodInvokeList(List<Anchor> list){
		List<MessageInvokeRecord> messageInvokeRecords = new ArrayList<MessageInvokeRecord>();
		for(int i=0;i<list.size();i++){
			Anchor anchor = list.get(i);
			if(AgentConstants.METHOD_START==anchor.getPos()){
				MessageInvokeRecord mir = new MessageInvokeRecord(anchor.getDepth(),anchor.getTime(),anchor.getMethodName());
				messageInvokeRecords.add(mir);
			}else if(AgentConstants.METHOD_END==anchor.getPos()){
				setEndTime(messageInvokeRecords, anchor);
			}
		}
		return messageInvokeRecords;
	}
	
	public static void setEndTime(List<MessageInvokeRecord> list, Anchor anchor){
		for(int i = list.size()-1;i>=0;i--){
			MessageInvokeRecord mir = list.get(i);
			if(mir.getPeriod()!=Long.MAX_VALUE)
				continue;
			if(mir.getDepth()>anchor.getDepth())
				continue;
			mir.setPeriod(anchor.getTime()-mir.getStartTime());
			if(anchor.getTime()<0){//抛异常了
				mir.setThrowException(true);
			}
			return;
		}
	}
	
	public static MethodChain buildMethodChain(List<MessageInvokeRecord> list){
		MethodChain mc = new MethodChain(null);
		buildMethodChain(mc, 0, list);
		return mc;
	}
	
	public static void buildMethodChain(MethodChain mc, int depth, List<MessageInvokeRecord> list){
		List<MethodChain> methodChains = mc.getMethodChains();
		List<MessageInvokeRecord> save = new ArrayList<MessageInvokeRecord>();
		List<List<MessageInvokeRecord>> mirlist = splitByDepths(list, depth+1, save);
		if(save.size()==0)
			return;
		for(int index=0; index<mirlist.size(); index++){
			MessageInvokeRecord mir = save.get(index);
			MethodChain singleMc = new MethodChain(mir);
			methodChains.add(singleMc);
			buildMethodChain(singleMc, depth+1,mirlist.get(index));
		}
	}
	
	public static List<List<MessageInvokeRecord>> splitByDepths(List<MessageInvokeRecord> list, int depth, List<MessageInvokeRecord> save){
		List<List<MessageInvokeRecord>> res = new ArrayList<List<MessageInvokeRecord>>();
		if(list.size()==0)
			return res;
		int last = 0;
		for(int i=1; i<list.size(); i++){
			MessageInvokeRecord mir = list.get(i);
			if(mir.getDepth()==depth){
				save.add(list.get(last));
				res.add(list.subList(last+1, i));
				last = i;
			}
		}
		save.add(list.get(last));
		res.add(list.subList(last+1,list.size()));
		return res;
	}
	
	public static void printMethodChain(MethodChain mc, int depth){
		if(mc.getMir()!=null){
			printStar(depth);
			MessageInvokeRecord mir = mc.getMir();
			StringBuilder sb = new StringBuilder(mc.getMir().getMethodName()+" ");
			if(mir.isThrowException()){
				sb.append("Throw-Exception");
			}else{
				sb.append(mc.getMir().getPeriod());
			}
			System.out.println(sb.toString());
		}
			
		for(MethodChain singleMc : mc.getMethodChains()){
			printMethodChain(singleMc, depth+1);
		}
	}
	
	public static void printStar(int depth){
		for(int i=0;i<(depth-1)*5;i++)
			System.out.print("*");
		
	}
	
}
